// Copyright (c) 2023 Presto Labs Pte. Ltd.
// Author: fengyang

#pragma once

#include <iostream>
#include <memory>
#include <optional>
#include <set>
#include <sstream>
#include <string>

#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include "coin2/exchange/base/symbology/spot_product.h"
#include "coin2/exchange/base/symbology/util.h"
#include "coin2/exchange/mercado/symbology/currency.h"

namespace coin2::exchange::mercado::symbology {

using MercadoCurrency = coin2::exchange::mercado::symbology::MercadoCurrency;

class MercadoProduct : public base::symbology::SpotProduct<MercadoCurrency, MercadoProduct>,
                       public base::symbology::ProductFromStrMixin<MercadoProduct> {
 public:
  typedef base::symbology::SpotProduct<MercadoCurrency, MercadoProduct> BaseProduct;

 public:
  MercadoProduct(const MercadoCurrency& base, const MercadoCurrency& quote)
      : SpotProduct(
            base,
            quote,
            ToNativeProduct(base, quote),
            ToFeedProduct(base, quote),
            base::market::ExchangeType::Mercado) {}

 public:
  static std::unique_ptr<MercadoProduct> FromNativeStrImpl(
      const std::string& native_product,
      int64_t resolve_timestamp) {
    std::unique_ptr<MercadoCurrency> base_ptr;
    std::unique_ptr<MercadoCurrency> quote_ptr;
    bool success = ParseNativeProduct(native_product, resolve_timestamp, &base_ptr, &quote_ptr);
    if (success) {
      return std::make_unique<MercadoProduct>(*base_ptr, *quote_ptr);
    } else {
      return {};
    }
  }

  static std::string ToRestNativeProduct(
      const base::symbology::ICurrency& base,
      const base::symbology::ICurrency& quote) {
    using base::market::MarketType;
    std::stringstream ss;
    ss << base.native_currency(MarketType::Spot) << "-" << quote.native_currency(MarketType::Spot);
    return ss.str();
  }

  static std::string RestToNativeProduct(std::string_view native_symbol) {
    std::vector<std::string> pairs = Split(native_symbol, "-");
    CHECK_EQ(pairs.size(), 2);
    return pairs[1] + pairs[0];
  }

 private:
  static bool ParseNativeProduct(
      const std::string& product_str,
      int64_t resolve_timestamp,
      std::unique_ptr<MercadoCurrency>* base_ptr,
      std::unique_ptr<MercadoCurrency>* quote_ptr) {
    std::string base;
    std::string quote;
    if (product_str.find("-") != std::string::npos) {
      std::vector<std::string> splitted = Split(product_str, "-");
      if (splitted.size() != 2) {
        return false;
      }

      base = splitted[0];
      quote = splitted[1];
    } else {
      static const char* quotes[] = {
          "BRL"
      };

      for (std::string quote_cand : quotes) {
        if (StringStartsWith(product_str, quote_cand)) {
          base = product_str.substr(quote_cand.size());
          quote = quote_cand;
          break;
        }
      }
    }

    *base_ptr = MercadoCurrency::FromNativeStrNoThrow(base, resolve_timestamp);
    *quote_ptr = MercadoCurrency::FromNativeStrNoThrow(quote, resolve_timestamp);

    if(*base_ptr == nullptr || *quote_ptr == nullptr){
      return false;
    }

    return true;
  }

  static std::string ToNativeProduct(const MercadoCurrency& base, const MercadoCurrency& quote) {
    using base::market::MarketType;
    std::stringstream ss;
    ss << base.native_currency(MarketType::Spot) << "-" << quote.native_currency(MarketType::Spot);
    return ss.str();
  }

  static std::string ToFeedProduct(const MercadoCurrency& base, const MercadoCurrency& quote) {
    using base::market::MarketType;
    std::stringstream ss;
    ss << quote.native_currency(MarketType::Spot) << base.native_currency(MarketType::Spot);
    return ss.str();
  }
};

}  // namespace coin2::exchange::mercado::symbology
